package com.eedi.framework.permission.service;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.google.common.annotations.VisibleForTesting;
import com.eedi.framework.common.enums.CommonStatusEnum;
import com.eedi.framework.common.enums.UserTypeEnum;
import com.eedi.framework.common.pojo.PageResult;
import com.eedi.framework.common.util.collection.CollectionUtils;
import com.eedi.framework.common.util.object.BeanUtils;
import com.eedi.framework.permission.controller.sys.vo.role.SysRoleSaveReq;
import com.eedi.framework.permission.controller.sys.vo.role.SysRoleSimpleResp;
import com.eedi.framework.permission.dal.SysPermissionCacheKeyConstants;
import com.eedi.framework.permission.dal.dataobject.SysRoleDO;
import com.eedi.framework.permission.dal.mapper.SysRoleMapper;
import com.eedi.framework.permission.dal.mapper.SysUserRoleMapper;
import com.eedi.framework.permission.enums.SysRoleCodeEnum;
import com.eedi.framework.permission.enums.SysRoleTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.eedi.framework.permission.enums.SysDataScopeEnum;

import javax.annotation.Resource;
import java.util.*;

import com.eedi.framework.permission.controller.sys.vo.role.SysRolePageReq;

import static com.eedi.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.eedi.framework.common.util.collection.CollectionUtils.convertMap;
import static com.eedi.framework.permission.enums.SysPermissionErrorCodeConstants.*;

import com.eedi.framework.permission.controller.sys.vo.role.SysRoleUpReq;

/**
 * 角色 Service 实现类
 *
 * @author 永聚长青源码
 */
@Service
@Slf4j
public class SysRoleServiceImpl implements SysRoleService {
    @Resource
    private SysRoleMapper sysRoleMapper;

    @Resource
    private SysUserRoleMapper sysUserRoleMapper;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public String createRole(SysRoleSaveReq createReqVO) {
        // 校验角色
        validateRoleDuplicate(createReqVO, null);
        // 插入到数据库
        SysRoleDO role = BeanUtils.toBean(createReqVO, SysRoleDO.class);
        role.setSysRoleType(SysRoleTypeEnum.CUSTOM);
        role.setSysRoleStatus(CommonStatusEnum.ENABLE);
        role.setSysRoleCode(SysRoleCodeEnum.DEFAULT.getCode());
        role.setSysRoleSort(0);
        // 默认可查看所有数据。原因是，可能一些项目不需要项目权限
        role.setSysRoleDataScope(SysDataScopeEnum.ALL);
        sysRoleMapper.insert(role);
        return role.getSysRoleId();
    }

    @Override
    @CacheEvict(value = SysPermissionCacheKeyConstants.ROLE, key = "#updateReqVO.sysRoleId")
    public void updateRole(SysRoleUpReq updateReqVO) {
        // 校验是否可以更新
        validateRoleForUpdate(updateReqVO.getSysRoleId());
        // 校验角色的唯一字段是否重复
        validateRoleDuplicate(updateReqVO, updateReqVO.getSysRoleId());
        // 更新到数据库
        SysRoleDO updateObj = BeanUtils.toBean(updateReqVO, SysRoleDO.class);
        sysRoleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = SysPermissionCacheKeyConstants.ROLE, key = "#sysRoleId")
    public void updateRoleStatus(String sysRoleId, CommonStatusEnum status) {
        // 校验是否可以更新
        validateRoleForUpdate(sysRoleId);

        // 更新状态
        SysRoleDO updateObj = new SysRoleDO().setSysRoleId(sysRoleId).setSysRoleStatus(status);
        sysRoleMapper.updateById(updateObj);
    }

    @Override
    @CacheEvict(value = SysPermissionCacheKeyConstants.ROLE, key = "#sysRoleId")
    public void updateRoleDataScope(String sysRoleId, SysDataScopeEnum dataScope, Set<String> dataScopeDeptIds) {
        // 校验是否可以更新
        validateRoleForUpdate(sysRoleId);

        // 更新数据范围
        SysRoleDO updateObject = new SysRoleDO();
        updateObject.setSysRoleId(sysRoleId);
        updateObject.setSysRoleDataScope(dataScope);
        updateObject.setSysRoleDataScopeDeptIds(dataScopeDeptIds);
        sysRoleMapper.updateById(updateObject);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = SysPermissionCacheKeyConstants.ROLE, key = "#sysRoleId")
    public void deleteRole(String sysRoleId) {
        // 校验是否可以更新
        validateRoleForUpdate(sysRoleId);
        // 标记删除
        sysRoleMapper.deleteById(sysRoleId);
    }

    /**
     * 校验角色的唯一字段是否重复
     * <p>
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param sysRoleSaveReq
     * @param roleId
     */
    @VisibleForTesting
    void validateRoleDuplicate(SysRoleSaveReq sysRoleSaveReq, String roleId) {
        // 0. 超级管理员、租户管理员，不允许创建
//        if (SysRoleCodeEnum.isImplAdmin(sysRoleSaveReq.getSysRoleCode()) || SysRoleCodeEnum.isSysAdmin(sysRoleSaveReq.getSysRoleCode())) {
//            throw exception(SYS_ROLE_ADMIN_CODE_ERROR, sysRoleSaveReq.getSysRoleCode());
//        }
        // 1. 该 name 名字被其它角色所使用
        SysRoleDO role = sysRoleMapper.selectByName(sysRoleSaveReq.getSysRoleName(), UserTypeEnum.MEMBER_SYS);
        if (role != null && !role.getSysRoleId().equals(roleId)) {
            throw exception(SYS_ROLE_NAME_DUPLICATE, sysRoleSaveReq.getSysRoleName());
        }
//        // 2. 是否存在相同编码的角色
//        if (!StringUtils.hasText(sysRoleSaveReq.getSysRoleCode())) {
//            return;
//        }
        // 该 code 编码被其它角色所使用
//        role = sysRoleMapper.selectByCode(sysRoleSaveReq.getSysRoleCode(), UserTypeEnum.MEMBER_SYS);
//        if (role != null && !role.getSysRoleId().equals(roleId)) {
//            throw exception(SYS_ROLE_CODE_DUPLICATE, sysRoleSaveReq.getSysRoleCode());
//        }
    }

    /**
     * 校验角色的唯一字段是否重复
     * <p>
     * 1. 是否存在相同名字的角色
     * 2. 是否存在相同编码的角色
     *
     * @param sysRoleSaveReq
     * @param roleId
     */
    @VisibleForTesting
    void validateRoleDuplicate(SysRoleUpReq sysRoleSaveReq, String roleId) {
        // 0. 超级管理员、租户管理员，不允许创建
//        if (SysRoleCodeEnum.isImplAdmin(sysRoleSaveReq.getSysRoleCode()) || SysRoleCodeEnum.isSysAdmin(sysRoleSaveReq.getSysRoleCode())) {
//            throw exception(SYS_ROLE_ADMIN_CODE_ERROR, sysRoleSaveReq.getSysRoleCode());
//        }
        // 1. 该 name 名字被其它角色所使用
        SysRoleDO role = sysRoleMapper.selectByName(sysRoleSaveReq.getSysRoleName(), UserTypeEnum.MEMBER_SYS);
        if (role != null && !role.getSysRoleId().equals(roleId)) {
            throw exception(SYS_ROLE_NAME_DUPLICATE, sysRoleSaveReq.getSysRoleName());
        }
        // 2. 是否存在相同编码的角色
//        if (!StringUtils.hasText(sysRoleSaveReq.getSysRoleCode())) {
//            return;
//        }
        // 该 code 编码被其它角色所使用
//        role = sysRoleMapper.selectByCode(sysRoleSaveReq.getSysRoleCode(), UserTypeEnum.MEMBER_SYS);
//        if (role != null && !role.getSysRoleId().equals(roleId)) {
//            throw exception(SYS_ROLE_CODE_DUPLICATE, sysRoleSaveReq.getSysRoleCode());
//        }
    }

    /**
     * 校验角色是否可以被更新
     *
     * @param roleId 角色编号
     */
    @VisibleForTesting
    void validateRoleForUpdate(String roleId) {
        SysRoleDO sysRoleDO = sysRoleMapper.selectById(roleId);
        if (sysRoleDO == null) {
            throw exception(SYS_ROLE_NOT_EXISTS);
        }
        // 内置角色，不允许删除
        if (SysRoleTypeEnum.SYSTEM.equals(sysRoleDO.getSysRoleType())) {
            throw exception(SYS_ROLE_CAN_NOT_UPDATE_SYSTEM_TYPE_ROLE);
        }
    }

    @Override
    public SysRoleDO getRole(String roleId) {
        return sysRoleMapper.selectById(roleId);
    }

    @Override
    @Cacheable(value = SysPermissionCacheKeyConstants.ROLE, key = "#sysRoleId",
            unless = "#result == null")
    public SysRoleDO getRoleFromCache(String sysRoleId) {
        return sysRoleMapper.selectById(sysRoleId);
    }


    @Override
    public List<SysRoleDO> getRoleListByStatus(Collection<CommonStatusEnum> statuses) {
        return sysRoleMapper.selectListByStatus(statuses);
    }

    @Override
    public List<SysRoleDO> getRoleList() {
        return sysRoleMapper.selectList();
    }

    @Override
    public List<SysRoleDO> getRoleList(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return Collections.emptyList();
        }
        return sysRoleMapper.selectBatchIds(roleIds);
    }

    @Override
    public List<SysRoleSimpleResp> getRoleSimpleList(Collection<String> sysRoleIds) {
        return sysRoleMapper.selectByRoleIds(sysRoleIds);
    }

    @Override
    public List<SysRoleDO> getRoleListFromCache(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return Collections.emptyList();
        }
        // 这里采用 for 循环从缓存中获取，主要考虑 Spring CacheManager 无法批量操作的问题
        SysRoleServiceImpl self = getSelf();
        return CollectionUtils.convertList(roleIds, self::getRoleFromCache);
    }

    @Override
    public PageResult<SysRoleDO> getRolePage(SysRolePageReq reqVO) {
        return sysRoleMapper.selectPage(reqVO);
    }

    @Override
    public boolean hasAnySuperAdmin(Collection<String> roleIds) {
        if (CollectionUtil.isEmpty(roleIds)) {
            return false;
        }
        SysRoleServiceImpl self = getSelf();
        return roleIds.stream().anyMatch(roleId -> {
            SysRoleDO role = self.getRoleFromCache(roleId);
            return role != null
                    && (SysRoleCodeEnum.isImplAdmin(role.getSysRoleCode())
                    || SysRoleCodeEnum.isSysAdmin(role.getSysRoleCode()));
        });
    }

    @Override
    public void validateRoleList(Collection<String> roleIds) {
        if (CollUtil.isEmpty(roleIds)) {
            return;
        }
        // 获得角色信息
        List<SysRoleDO> roles = sysRoleMapper.selectBatchIds(roleIds);
        Map<String, SysRoleDO> roleMap = convertMap(roles, SysRoleDO::getSysRoleId);
        // 校验
        roleIds.forEach(roleId -> {
            SysRoleDO role = roleMap.get(roleId);
            if (role == null) {
                throw exception(SYS_ROLE_NOT_EXISTS);
            }
            if (!CommonStatusEnum.ENABLE.equals(role.getSysRoleStatus())) {
                throw exception(SYS_ROLE_IS_DISABLE, role.getSysRoleName());
            }
        });
    }

    @Override
    public List<SysRoleSimpleResp> getRoleByCode(String sysRoleCode) {
        if (StrUtil.isBlank(sysRoleCode)) {
            return List.of();
        }
        return sysRoleMapper.getByCode(sysRoleCode);
    }

    @Override
    public List<String> getSysUserIdByDefaultSysRoleCode() {
        List<SysRoleSimpleResp> sysRoleSimples = sysRoleMapper.selectSysRoleCode(SysRoleCodeEnum.DEFAULT);
        List<String> sysRoleIds = sysRoleSimples.stream().map(SysRoleSimpleResp::getSysRoleId).toList();
        if (CollUtil.isEmpty(sysRoleIds)){
            return List.of();
        }
        return sysUserRoleMapper.getBySysRoleId(sysRoleIds);
    }


    /**
     * 获得自身的代理对象，解决 AOP 生效问题
     *
     * @return 自己
     */
    private SysRoleServiceImpl getSelf() {
        return SpringUtil.getBean(getClass());
    }

}
